home *** CD-ROM | disk | FTP | other *** search
- #include "machdep.h"
- #include "timer.h"
- #include "mbuf.h"
- #include "netuser.h"
- #include "internet.h"
- #include "tcp.h"
-
- int16 tcp_mss = DEF_MSS; /* Maximum segment size to be sent with SYN */
-
- /* Send a segment on the specified connection. One gets sent only
- * if there is data to be sent or if "force" is non zero
- */
- void
- tcp_output(tcb)
- register struct tcb *tcb;
- {
- struct pseudo_header ph;
- struct mbuf *hbp;
- int16 hsize; /* Size of header */
- struct tcp_header *tcph;
- struct mss *mssp;
- int16 ssize; /* Size of current segment being sent,
- * including SYN and FIN flags */
- int16 dsize; /* Size of segment less SYN and FIN */
- int16 usable; /* Usable window */
- int16 sent; /* Sequence count (incl SYN/FIN) already in the pipe */
-
- if(tcb == NULLTCB)
- return;
-
- switch(tcb->state){
- case LISTEN:
- case CLOSED:
- return; /* Don't send anything */
- }
- for(;;){
- sent = tcb->snd.ptr - tcb->snd.una;
-
- /* If this is a retransmission, send only the oldest segment
- * (first-only retransmission policy)
- */
- if(tcb->retry != 0 && sent != 0)
- break;
-
- /* There can only be one outstanding segment in this state
- * since the other end would reject any segment without SYN
- */
- if(tcb->state == SYN_SENT && sent != 0)
- break;
- if(tcb->snd.wnd == 0){
- /* Allow only one closed-window probe at a time */
- if(sent != 0)
- break;
- /* Force a closed-window probe */
- usable = 1;
- } else {
- /* usable window = offered window - unacked bytes in transit */
- usable = tcb->snd.wnd - sent;
-
- /* John Nagle's "single outstanding segment" rule.
- * Allow only one segment in the pipeline unless there is enough
- * unsent data to form at least one maximum-sized segment.
- */
- if(sent != 0 && tcb->sndcnt - sent < tcb->mss){
- usable = 0;
- }
- /* Silly window avoidance. Don't send anything if the usable window
- * is less than a quarter of the offered window.
- * This test comes into play only when the offered window is at
- * least 4 times the MSS; otherwise Nagle's test is sufficient
- * to prevent SWS.
- */
- else if(usable < tcb->snd.wnd/4){
- usable = 0;
- }
- }
- /* Compute size of segment to send. This is either the usable
- * window, the mss, or the amount we have on hand, whichever is less.
- * (I don't like optimistic windows)
- */
- ssize = min(tcb->sndcnt - sent,usable);
- ssize = min(ssize,tcb->mss);
- dsize = ssize;
-
- if(ssize == 0 && tcb->force == 0)
- break; /* No need to send anything */
-
- /* Determine size of TCP header and allocate mbuf.
- * If sending SYN, allow space for the MSS option
- */
- switch(tcb->state){
- case SYN_SENT:
- case SYN_RECEIVED:
- hsize = sizeof(struct tcp_header) + sizeof(struct mss);
- break;
- default:
- hsize = sizeof(struct tcp_header);
- break;
- }
- if((hbp = alloc_mbuf(hsize)) == NULLBUF)
- break; /* No room to form a packet */
-
- tcb->force = 0; /* Only one forced segment! */
- hbp->cnt = hsize;
-
- tcph = (struct tcp_header *)hbp->data;
- tcph->source = htons(tcb->conn.local.port);
- tcph->dest = htons(tcb->conn.remote.port);
- tcph->offset = hsize/sizeof(int32) << DSHIFT;
- tcph->flags = 0;
-
- /* Set the SYN and ACK flags according to the state we're in. It is
- * assumed that if this segment is associated with a state transition,
- * then the state change will already have been made. This allows
- * this routine to be called from a retransmission timeout with
- * force=1.
- * If SYN is being sent, adjust the dsize counter so we'll
- * try to get the right amount of data off the send queue.
- */
- switch(tcb->state){
- case SYN_SENT:
- case SYN_RECEIVED:
- if(tcb->snd.ptr == tcb->iss){
- tcph->flags = SYN;
- dsize--;
- }
- /* Also send MSS */
- mssp = (struct mss *)(tcph + 1);
- mssp->kind = MSS_KIND;
- mssp->length = MSS_LENGTH;
- mssp->mss = htons(tcp_mss);
- }
- switch(tcb->state){
- case SYN_RECEIVED:
- case ESTABLISHED:
- case CLOSE_WAIT:
- case FINWAIT2:
- case TIME_WAIT:
- case CLOSING:
- case FINWAIT1:
- case LAST_ACK:
- tcph->flags |= ACK;
- break;
- }
- tcph->seq = htonl(tcb->snd.ptr);
- tcph->ack = htonl(tcb->rcv.nxt);
- tcph->wnd = htons(tcb->rcv.wnd);
- tcph->checksum = 0;
- tcph->up = 0;
-
- /* Now try to extract some data from the send queue.
- * Since SYN and FIN occupy sequence space and are reflected
- * in sndcnt but don't actually sit in the send queue,
- * dup_p will return one less than dsize if a FIN needs to be sent.
- */
- if(dsize != 0){
- if(dup_p(&hbp->next,tcb->sndq,sent,dsize) != dsize){
- /* We ran past the end of the send queue; send a FIN */
- tcph->flags |= FIN;
- dsize--;
- }
- }
- /* If the entire send queue will now be in the pipe, set the
- * push flag
- */
- if(dsize != 0 && sent + ssize == tcb->sndcnt)
- tcph->flags |= PSH;
-
- tcb->snd.ptr += ssize;
- /* If this is the first transmission of a range of sequence
- * numbers, record it so we'll accept acknowledgments
- * for it later
- */
- if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
- tcb->snd.nxt = tcb->snd.ptr;
- /* Fill in fields of pseudo IP header */
- ph.source = tcb->conn.local.address;
- ph.dest = tcb->conn.remote.address;
- ph.protocol = TCP_PTCL;
- ph.zero = 0;
- ph.length = hsize + dsize;
-
- /* Compute checksum over pseudo-header, TCP header and data,
- * and pass it off to IP
- */
- tcph->checksum = cksum(&ph,hbp,ph.length);
-
- /* If we're sending some data or flags, start retransmission
- * timer if it isn't already running.
- */
- if(ssize != 0){
- if(tcb->timer.state != TIMER_RUN){
- /* Never initialize the timer with zero; it won't run! */
- tcb->timer.start = max(tcb->timer.start,1);
- start_timer(&tcb->timer);
- }
- /* If round trip timer isn't running, start it */
- if(seq_ge(tcb->snd.una,tcb->rttseq))
- tcb->rttseq = tcb->snd.ptr;
- }
- ip_send(tcb->conn.local.address,tcb->conn.remote.address,
- TCP_PTCL,tcb->tos,0,hbp,ph.length,0,0);
- }
- }
-